\bibliographystyle{gbt7714-2005}

\chapter{PHP中的函数}
\epigraphhead[60]{\epigraph{Don't Repeat Yourself.}{\textit{Andrew Hunt《The Pragmatic Programmer》}}}
函数（function），是一种子程序，利用函数名称，可以接收回传值。函数过程中的语句用于完成某些有意义的工作。

\section{定义函数}

\begin{lstlisting}
<?php
function foo($arg_1, $arg_2, /* ..., */ $arg_n)
{
    echo "Example function.\n";
    return $retval;
}
?>
\end{lstlisting}

任何有效的 PHP 代码都有可能出现在函数内部，甚至包括其它函数和类定义。函数名和 PHP 中的其它标识符命名规则相同。而且，函数无需在调用之前被定义。

\section{函数的参数}
通过参数列表可以传递信息到函数，即以逗号作为分隔符的表达式列表。参数是从左向右求值的。

PHP 支持按值传递参数（默认），通过引用传递参数以及默认参数。也支持可变长度参数列表。

\subsection{通过引用传递函数参数}
默认情况下，函数参数通过值传递。如果希望允许函数修改它的参数值，必须通过引用传递参数。实现引用传参的方法是，在函数定义中该参数的前面加上符号 \&：

\begin{lstlisting}
<?php
function add_some_extra(&$string)
{
    $string .= 'and something extra.';
}
$str = 'This is a string, ';
add_some_extra($str);
echo $str;    // outputs 'This is a string, and something extra.'
?>
\end{lstlisting}

\subsection{设定参数默认值}
PHP设定参数默认值的语法是在定义变量时直接赋值：

\begin{lstlisting}
<?php
function makecoffee($type = "cappuccino")
{
    return "Making a cup of $type.\n";
}
echo makecoffee();
echo makecoffee(null);
echo makecoffee("espresso");
?>
\end{lstlisting}

上述代码将输出：

\begin{lstlisting}
Making a cup of cappuccino.
Making a cup of .
Making a cup of espresso.
\end{lstlisting}

注意当使用默认参数时，任何默认参数必须放在任何非默认参数的右侧；否则，函数将不会按照预期的情况工作。

\subsection{可变数量的参数列表}
PHP 在用户自定义函数中支持可变数量的参数列表。在 PHP 5.6 及以上的版本中，由 ... 语法实现；在 PHP 5.5 及更早版本中，使用函数 func\_num\_args()，func\_get\_arg()，和 func\_get\_args() 。

\begin{lstlisting}
<?php
function sum(...$numbers) {
    $acc = 0;
    foreach ($numbers as $n) {
        $acc += $n;
    }
    return $acc;
}

echo sum(1, 2, 3, 4);
?>
\end{lstlisting}
以上代码会输出1 2 3 4 的和，即10。

在早期版本中，要实现可变数量的参数，需要这样写：

\begin{lstlisting}
<?php
function sum() {
    $acc = 0;
    foreach (func_get_args() as $n) {
        $acc += $n;
    }
    return $acc;
}

echo sum(1, 2, 3, 4);
?>
\end{lstlisting}

\section{返回值}
值通过使用可选的返回语句return返回，如果没有return语句，则函数返回NULL。可以返回包括数组和对象的任意类型。返回语句会立即中止函数的运行，并且将控制权交回调用该函数的代码行。

\begin{lstlisting}
<?php
function square($num)
{
    return $num * $num;
}
echo square(4);   // outputs '16'.
?>
\end{lstlisting}

若要返回多个值，可将这些值以数组的形式返回给调用者。

\subsection{指定返回值的类型}
在PHP7中，可以指定返回值的类型，具体演示如下：

\begin{lstlisting}
<?php
function sum($a, $b): float {
    return $a + $b;
}

// Note that a float will be returned.
var_dump(sum(1, 2));
?>
\end{lstlisting}
上述代码将输出float(3)

\section{可变函数}
PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号，PHP 将寻找与变量的值同名的函数，并且尝试执行它。可变函数可以用来实现包括回调函数，函数表在内的一些用途。

\begin{lstlisting}
<?php
function foo() {
    echo "In foo()<br />\n";
}

function bar($arg = '') {
    echo "In bar(); argument was '$arg'.<br />\n";
}

// 使用 echo 的包装函数
function echoit($string)
{
    echo $string;
}

$func = 'foo';
$func();        // This calls foo()

$func = 'bar';
$func('test');  // This calls bar()

$func = 'echoit';
$func('test');  // This calls echoit()
?>
\end{lstlisting}

\section{匿名函数}
匿名函数（Anonymous functions），也叫闭包函数（closures），允许临时创建一个没有指定名称的函数。匿名函数经常用作回调函数（callback）参数的值。当然，也有其它应用的情况。匿名函数目前是通过 Closure 类来实现的。闭包函数也可以作为变量的值来使用。PHP 会自动把此种表达式转换成内置类 Closure 的对象实例。\cite{PHPGroup，2016a}

\subsection{匿名函数的定义}
定义匿名函数时，注意两点，一是函数没有名称，二是函数体结束后加个分号。
\begin{lstlisting}
<?php
$greet = function($name)
{
    printf("Hello %s\r\n", $name);
};

$greet('World');
$greet('PHP');
?>
\end{lstlisting}

从上例中可以看到，匿名函数因为没有名字，如果要使用它，需要将其返回给一个变量。匿名函数也像普通函数一样可以声明参数，调用方法也相同。

\subsection{从父作用域继承变量}
闭包可以从父作用域中继承变量。 任何此类变量都应该用 use 语言结构传递进去。

\begin{lstlisting}
<?php
$message = 'hello';

// 没有 "use"
$example = function () {
	var_dump($message);
};
// 显示警告信息，显示NULL
echo $example();

// 使用 use 继承 $message
$example = function () use ($message) {
	var_dump($message);
};
// 输出结果为hello
echo $example();

// 使用use继承变量时，当变量改变后
// 再次调用匿名函数时并不能使用最新值。
$message = 'world';
// 输出结果不是world，而是hello
echo $example();

$message = 'hello';

// use传递参数时，也可以采用引用传值
$example = function () use (&$message) {
	var_dump($message);
};
echo $example();
$message = 'world';
echo $example();

// 闭包函数也可以接受参数值
$example = function ($arg) use ($message) {
	var_dump($arg . ' ' . $message);
};
$example("hello");
?>
\end{lstlisting}

闭包的父作用域是定义该闭包的函数，而不一定是调用它的函数。

\subsection{返回匿名函数}
如果将匿名函数返回给外界，匿名函数会保存use所引用的变量，而外界则不能得到这些变量，这样形成‘闭包’：

\begin{lstlisting}
function getMoneyFunc() {
    $rmb = 1;
    $func = function() use ( &$rmb ) {
        echo $rmb;
        //把$rmb的值加1
        $rmb++;
    };
    return $func;
}

$getMoney = getMoneyFunc();
$getMoney();
$getMoney();
$getMoney();

//输出：
//1
//2
//3
\end{lstlisting}

% \section{回调函数}

% \section{递归函数}

\bibliography{../bib/yangjh}
